﻿using System;
using System.Linq;

namespace StreetPacMan.Server.Rules
{
    //[PluginFamily]
    public interface IApplesEatingRule
    {
        void DoSideEffects(Apple[] eatenApples, Player pacMan);
    }

    public class AddEventForApplesEatingRule : IApplesEatingRule
    {
        public void DoSideEffects(Apple[] eatenApples, Player pacMan)
        {
            foreach (var eatenApple in eatenApples)
            {
                pacMan.Game.Events.Add(
                    new Event
                        {
                            GameId = pacMan.GameId,
                            ReceivedAt = DateTime.Now,
                            TypeEnum = EventType.PacManAteApple,
                            Description = string.Format("{0} ate {1} apple", pacMan.Name, eatenApple.KindEnum)
                        });
            }
        }
    }

    //[Pluggable("GameOverPacMenWinRule"), UsedByIoc]
    public class GameOverPacMenWinRule : IApplesEatingRule
    {
        public void DoSideEffects(Apple[] eatenApples, Player pacMan)
        {
            var activeApplesIds = pacMan.Game
                .Apples
                .Where(x => x.GameId == pacMan.GameId)
                .Where(x => x.State == (int) AppleState.Active)
                .Select(x => x.Id)
                .ToArray();
            var eatenApplesIds = eatenApples.Select(x => x.Id).ToArray();
            var allremainingActiveApplesAreEatenNow = activeApplesIds.All(x => eatenApplesIds.Contains(x));

            if(allremainingActiveApplesAreEatenNow)
            {
                pacMan.Game.AddGameEvent(EventType.GameEnded,
                                               string.Format("Game Over! PacMen win with {0} points!", pacMan.Game.Score),
                                               pacMan.Id
                    );
                pacMan.Game.State = (int)GameState.GameOver;
                pacMan.Game.StateChangeTime = DateTime.Now;
            }
        }
    }

    //[Pluggable("ChangeGameStateAndMessageIfEatenPowerPelletRule"), UsedByIoc]
    public class ChangeGameStateAndMessageIfEatenPowerPelletRule : IApplesEatingRule
    {
        public void DoSideEffects(Apple[] eatenApples, Player pacMan)
        {
            bool supperApplesEaten = eatenApples.Any(x => x.Kind == (int)AppleKind.Super);

            if (supperApplesEaten)
            {
                if (pacMan.Game.State != (int)GameState.GameOver)
                {
                    pacMan.Game.State = (int)GameState.SuperPacMan;
                    pacMan.Game.StateChangeTime = DateTime.Now;

                    pacMan.Game.AddGameEvent(
                        EventType.GameStateIsSuperPacMan,
                        pacMan.Name + " has eaten a power pellet!",
                        pacMan.Id
                        );
                }
            }
        }
    }

    //[Pluggable("AddScoreForEatenApplesRule"), UsedByIoc]
    public class AddScoreForEatenApplesRule : IApplesEatingRule
    {
        public void DoSideEffects(Apple[] eatenApples, Player pacMan)
        {
            int scoreAdded = eatenApples.Sum(x => PointsForAppleType((AppleKind) x.Kind));

            pacMan.Score += scoreAdded;
            pacMan.Game.Score += scoreAdded;
        }

        private int PointsForAppleType(AppleKind kind)
        {
          switch(kind)
          {
              case AppleKind.Normal: return PointsForEatingApple;
              case AppleKind.Super: return PointsForEatingSuperApple;
              case AppleKind.Cherry: return PointsForEatingCherry;
              default:
                  throw new ArgumentOutOfRangeException("kind");
          }
        }

        public const int PointsForEatingApple = 10;
        public const int PointsForEatingSuperApple = 50;
        public const int PointsForEatingCherry = 200;
    }

   // [Pluggable("RemoveEatenApplesFromBoardRule"), UsedByIoc]
    public class RemoveEatenApplesFromBoardRule : IApplesEatingRule
    {
        public void DoSideEffects(Apple[] eatenApples, Player pacMan)
        {
            foreach (var apple in eatenApples)
            {
                apple.RemovedAt = DateTime.Now;
                apple.RemovedByEntityId = pacMan.Id;
                apple.State = (int) AppleState.RemovedEaten;
            }
        }
    }
}